/*******************************************************************************
**
** Copyright (c) 2011 - The OWE Team
**
** This file is part of OWE.
**
** OWE is a free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; version
** version 2.1 of the License.
**
** OWE is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with OWE. If not, see <http://www.gnu.org/licenses/>.
**
*******************************************************************************/

#include <tut/tut.hpp>
#include <tut/tut_console_reporter.hpp>

#include <owe/authorelement.h>
#include <owe/contentelement.h>
#include <owe/descriptionelement.h>
#include <owe/element.h>
#include <owe/featureelement.h>
#include <owe/iconelement.h>
#include <owe/licenseelement.h>
#include <owe/nameelement.h>
#include <owe/preferenceelement.h>
#include <owe/ustring.h>
#include <owe/widgetelement.h>

#include "utests.h"

OWE_USE_NAMESPACE

namespace tut
{
    test_runner_singleton runner;

    struct WidgetElement_data {
        WidgetElement_data() {
            FakeNode *emptyNode = new FakeNode("widget");
            empty = static_cast<WidgetElement *>(Element::fromNode(emptyNode));
            delete emptyNode;

            FakeNode *fullNode = new FakeNode("widget");
            fullNode->addAttribute("id", "http://example.org/widget123");
            fullNode->addAttribute("version", "1.23");
            fullNode->addAttribute("height", "600");
            fullNode->addAttribute("width", "800");
            fullNode->addAttribute("viewmodes", "fullscreen floating");
            full = static_cast<WidgetElement *>(Element::fromNode(fullNode));
            delete fullNode;
        }

        ~WidgetElement_data() {
            delete empty;
            delete full;
        }

        WidgetElement *empty;
        WidgetElement *full;
    };

    typedef test_group<WidgetElement_data> testgroup;
    testgroup shared_ptr_group("WidgetElement");
    typedef testgroup::object testobject;

    template<>
    template<>
    void testobject::test<1>() {
        set_test_name("UString WidgetElement::id() const");

        ensure(empty->id() == "");
        ensure(full->id() == "http://example.org/widget123");
    }

    template<>
    template<>
    void testobject::test<2>() {
        set_test_name("UString WidgetElement::version() const");

        ensure(empty->version() == "");
        ensure(full->version() == "1.23");
    }

    template<>
    template<>
    void testobject::test<3>() {
        set_test_name("UString WidgetElement::height() const");

        ensure(empty->height() == "");
        ensure(full->height() == "600");
    }

    template<>
    template<>
    void testobject::test<4>() {
        set_test_name("UString WidgetElement::width() const");

        ensure(empty->width() == "");
        ensure(full->width() == "800");
    }

    template<>
    template<>
    void testobject::test<5>() {
        set_test_name("UString WidgetElement::viewModes() const");

        ensure(empty->viewModes() == "");
        ensure(full->viewModes() == "fullscreen floating");
    }

    template<>
    template<>
    void testobject::test<6>() {
        set_test_name("const NameElement *WidgetElement::name(const char *locale) const");

        ensure(empty->name() == 0);
        ensure(empty->name("pt-br") == 0);
        ensure(empty->addChild(0) == false);

        FakeNode *name;

        name = new FakeNode("name");
        name->addAttribute("xml:lang");
        NameElement *nameElement1 = static_cast<NameElement *>(Element::fromNode(name));
        ensure(full->addChild(nameElement1) == true);
        delete name;

        name = new FakeNode("name");
        name->addAttribute("xml:lang");
        NameElement *nameElement2 = static_cast<NameElement *>(Element::fromNode(name));
        ensure(full->addChild(nameElement2) == false);
        delete name;

        name = new FakeNode("name");
        name->addAttribute("xml:lang", "pt-br");
        NameElement *nameElement3 = static_cast<NameElement *>(Element::fromNode(name));
        ensure(full->addChild(nameElement3) == true);
        delete name;

        name = new FakeNode("name");
        name->addAttribute("xml:lang", "en-gb");
        NameElement *nameElement4 = static_cast<NameElement *>(Element::fromNode(name));
        ensure(full->addChild(nameElement4) == true);
        delete name;

        name = new FakeNode("name");
        name->addAttribute("xml:lang", "pt-br");
        NameElement *nameElement5 = static_cast<NameElement *>(Element::fromNode(name));
        ensure(full->addChild(nameElement5) == false);
        delete name;

        ensure(full->name() == nameElement1);
        ensure(full->name("pt-br") == nameElement3);
        ensure(full->name("en-gb") == nameElement4);
    }

    template<>
    template<>
    void testobject::test<7>() {
        set_test_name("const DescriptionElement *WidgetElement::description(const char *locale) const");

        ensure(empty->description() == 0);
        ensure(empty->description("pt-br") == 0);
        ensure(empty->addChild(0) == false);

        FakeNode *description;

        description = new FakeNode("description");
        description->addAttribute("xml:lang");
        DescriptionElement *descriptionElement1 = static_cast<DescriptionElement *>(Element::fromNode(description));
        ensure(full->addChild(descriptionElement1) == true);
        delete description;

        description = new FakeNode("description");
        description->addAttribute("xml:lang");
        DescriptionElement *descriptionElement2 = static_cast<DescriptionElement *>(Element::fromNode(description));
        ensure(full->addChild(descriptionElement2) == false);
        delete description;

        description = new FakeNode("description");
        description->addAttribute("xml:lang", "pt-br");
        DescriptionElement *descriptionElement3 = static_cast<DescriptionElement *>(Element::fromNode(description));
        ensure(full->addChild(descriptionElement3) == true);
        delete description;

        description = new FakeNode("description");
        description->addAttribute("xml:lang", "en-gb");
        DescriptionElement *descriptionElement4 = static_cast<DescriptionElement *>(Element::fromNode(description));
        ensure(full->addChild(descriptionElement4) == true);
        delete description;

        description = new FakeNode("description");
        description->addAttribute("xml:lang", "pt-br");
        DescriptionElement *descriptionElement5 = static_cast<DescriptionElement *>(Element::fromNode(description));
        ensure(full->addChild(descriptionElement5) == false);
        delete description;

        ensure(full->description() == descriptionElement1);
        ensure(full->description("pt-br") == descriptionElement3);
        ensure(full->description("en-gb") == descriptionElement4);
    }

    template<>
    template<>
    void testobject::test<8>() {
        set_test_name("const FeatureElementList WidgetElement::feature() const");

        FeatureElementList featureList;

        featureList = empty->feature();
        ensure(featureList.size() == 0);

        FakeNode *feature;

        feature = new FakeNode("feature");
        feature->addAttribute("name", "test1");
        FeatureElement *featureElement1 = static_cast<FeatureElement *>(Element::fromNode(feature));
        ensure(full->addChild(featureElement1) == true);
        delete feature;

        feature = new FakeNode("feature");
        feature->addAttribute("name", "test2");
        feature->addAttribute("required", "false");
        FeatureElement *featureElement2 = static_cast<FeatureElement *>(Element::fromNode(feature));
        ensure(full->addChild(featureElement2) == true);
        delete feature;

        // Same name as above, should be ignored
        feature = new FakeNode("feature");
        feature->addAttribute("name", "test2");
        feature->addAttribute("required", "true");
        FeatureElement *featureElement3 = static_cast<FeatureElement *>(Element::fromNode(feature));
        ensure(full->addChild(featureElement3) == false);
        delete feature;

        // Invalid, no name, should be ignored
        feature = new FakeNode("feature");
        FeatureElement *featureElement4 = static_cast<FeatureElement *>(Element::fromNode(feature));
        ensure(full->addChild(featureElement4) == false);
        delete feature;

        feature = new FakeNode("feature");
        feature->addAttribute("name", "test3");
        FeatureElement *featureElement5 = static_cast<FeatureElement *>(Element::fromNode(feature));
        ensure(full->addChild(featureElement5) == true);
        delete feature;

        featureList = full->feature();
        ensure(featureList.size() == 3);

        FeatureElementList::const_iterator it = featureList.begin();
        ensure(it->second == featureElement1);
        it++;
        ensure(it->second == featureElement2);
        it++;
        ensure(it->second == featureElement5);
        it++;
        ensure(it == featureList.end());
    }

    template<>
    template<>
    void testobject::test<9>() {
        set_test_name("const LicenseElement *WidgetElement::license(const char *locale) const");

        ensure(empty->license() == 0);

        FakeNode *license;

        license = new FakeNode("license");
        LicenseElement *licenseElement1 = static_cast<LicenseElement *>(Element::fromNode(license));
        ensure(full->addChild(licenseElement1) == true);
        delete license;

        license = new FakeNode("license");
        license->addAttribute("xml:lang", "pt-br");
        LicenseElement *licenseElement2 = static_cast<LicenseElement *>(Element::fromNode(license));
        ensure(full->addChild(licenseElement2) == true);
        delete license;

        license = new FakeNode("license");
        license->addAttribute("xml:lang", "pt-br");
        LicenseElement *licenseElement3 = static_cast<LicenseElement *>(Element::fromNode(license));
        ensure(full->addChild(licenseElement3) == false);
        delete license;

        license = new FakeNode("license");
        license->addAttribute("xml:lang", "en-us");
        LicenseElement *licenseElement4 = static_cast<LicenseElement *>(Element::fromNode(license));
        ensure(full->addChild(licenseElement4) == true);
        delete license;

        ensure(full->license() == licenseElement1);
        ensure(full->license("pt-br") == licenseElement2);
        ensure(full->license("en-us") == licenseElement4);
    }

    template<>
    template<>
    void testobject::test<10>() {
        set_test_name("const ContentElement *WidgetElement::content() const");

        ensure(empty->content() == 0);

        FakeNode *content;

        content = new FakeNode("content");
        content->addAttribute("src", "main.html");
        ContentElement *contentElement1 = static_cast<ContentElement *>(Element::fromNode(content));
        ensure(full->addChild(contentElement1) == true);
        delete content;

        content = new FakeNode("content");
        content->addAttribute("src", "index.xhtml");
        ContentElement *contentElement2 = static_cast<ContentElement *>(Element::fromNode(content));
        ensure(full->addChild(contentElement2) == false);
        delete content;

        content = new FakeNode("content");
        ContentElement *contentElement3 = static_cast<ContentElement *>(Element::fromNode(content));
        ensure(full->addChild(contentElement3) == false);
        delete content;

        ensure(full->content() == contentElement1);
    }

    template<>
    template<>
    void testobject::test<11>() {
        set_test_name("const AuthorElement *WidgetElement::author() const");

        ensure(empty->author() == 0);

        FakeNode *author;

        author = new FakeNode("author");
        AuthorElement *authorElement1 = static_cast<AuthorElement *>(Element::fromNode(author));
        ensure(full->addChild(authorElement1) == true);
        delete author;

        author = new FakeNode("author");
        AuthorElement *authorElement2 = static_cast<AuthorElement *>(Element::fromNode(author));
        ensure(full->addChild(authorElement2) == false);
        delete author;

        ensure(full->author() == authorElement1);
    }

    template<>
    template<>
    void testobject::test<12>() {
        set_test_name("const IconElementList WidgetElement::icon() const");

        IconElementList iconList = empty->icon();
        ensure(iconList.size() == 0);

        FakeNode *icon;

        icon = new FakeNode("icon");
        icon->addAttribute("src", "icon_aaa.png");
        IconElement *iconElement1 = static_cast<IconElement *>(Element::fromNode(icon));
        ensure(full->addChild(iconElement1) == true);
        delete icon;

        icon = new FakeNode("icon");
        icon->addAttribute("src", "icon_bbb.png");
        IconElement *iconElement2 = static_cast<IconElement *>(Element::fromNode(icon));
        ensure(full->addChild(iconElement2) == true);
        delete icon;

        icon = new FakeNode("icon");
        icon->addAttribute("src", "icon_bbb.png");
        IconElement *iconElement3 = static_cast<IconElement *>(Element::fromNode(icon));
        ensure(full->addChild(iconElement3) == false);
        delete icon;

        iconList = full->icon();
        ensure(iconList.size() == 2);

        IconElementList::const_iterator it = iconList.begin();

        // The icons were added alpha sorted by key, so we can
        // iterate over it like a sequential list.
        ensure(it->second == iconElement1);
        it++;
        ensure(it->second == iconElement2);
        it++;
        ensure(it == iconList.end());
    }

    template<>
    template<>
    void testobject::test<13>() {
        set_test_name("const PreferenceElementList WidgetElement::preference() const");

        PreferenceElementList preferenceList;

        preferenceList = empty->preference();
        ensure(preferenceList.size() == 0);

        FakeNode *preference;

        preference = new FakeNode("preference");
        preference->addAttribute("name", "test1");
        PreferenceElement *preferenceElement1 = static_cast<PreferenceElement *>(Element::fromNode(preference));
        ensure(full->addChild(preferenceElement1) == true);
        delete preference;

        preference = new FakeNode("preference");
        preference->addAttribute("name", "test2");
        preference->addAttribute("value", "metal");
        preference->addAttribute("readonly", "false");
        PreferenceElement *preferenceElement2 = static_cast<PreferenceElement *>(Element::fromNode(preference));
        ensure(full->addChild(preferenceElement2) == true);
        delete preference;

        // Invalid, no name, should be ignored
        preference = new FakeNode("preference");
        preference->addAttribute("value", "metal");
        preference->addAttribute("readonly", "false");
        PreferenceElement *preferenceElement3 = static_cast<PreferenceElement *>(Element::fromNode(preference));
        ensure(full->addChild(preferenceElement3) == false);
        delete preference;

        // Invalid, no name, should be ignored
        preference = new FakeNode("preference");
        PreferenceElement *preferenceElement4 = static_cast<PreferenceElement *>(Element::fromNode(preference));
        ensure(full->addChild(preferenceElement4) == false);
        delete preference;

        preference = new FakeNode("preference");
        preference->addAttribute("name", "test3");
        preference->addAttribute("readonly", "true");
        PreferenceElement *preferenceElement5 = static_cast<PreferenceElement *>(Element::fromNode(preference));
        ensure(full->addChild(preferenceElement5) == true);
        delete preference;

        // Already exists, should be ignored
        preference = new FakeNode("preference");
        preference->addAttribute("name", "test3");
        PreferenceElement *preferenceElement6 = static_cast<PreferenceElement *>(Element::fromNode(preference));
        ensure(full->addChild(preferenceElement6) == false);
        delete preference;

        preferenceList = full->preference();
        ensure(preferenceList.size() == 3);

        PreferenceElementList::const_iterator it = preferenceList.begin();
        ensure(it->second == preferenceElement1);
        it++;
        ensure(it->second == preferenceElement2);
        it++;
        ensure(it->second == preferenceElement5);
        it++;
        ensure(it == preferenceList.end());
    }
}

int main(void)
{
    tut::console_reporter reporter;
    tut::runner.get().set_callback(&reporter);
    tut::runner.get().run_tests();

    return reporter.all_ok() ? 0 : 1;
}
